package main;

import ligacaodados.CanalRuidoso;
import ligacaodados.Emissor;
import ligacaodados.Receptor;

public class Tarefa extends Thread {
	
	private static final int NUM_TRAMAS_ENVIADAS = 10000000;
	
	private int[] dados;
	private int opTecnica;
	private int tamanhoTrama;
	private double padraoErros;
	private ModoSimulacao ms;
	
	private double prob_tx_sem_erros;
	private double valor_esperado;
	private double prob_trama_corrigida;
	private double prob_erros_nao_detectados;
	
	
	public Tarefa(ModoSimulacao ms, int[] dados, int opTecnica, double padraoErros) {
		this.ms = ms;
		this.dados = dados;
		this.opTecnica = opTecnica;
		this.padraoErros = padraoErros;
	}

	@Override
	public void run() {
		int tramasRecebidaSemErros = 0;
		int tramaCom1Erro = 0;
		int tramasComErros = 0;
		int erros_nao_detectados = 0;
		int numErrosNaTrama = 0;
		
		for (int i = 0; i < NUM_TRAMAS_ENVIADAS; i++) {
			Emissor e = new Emissor(dados, opTecnica);
			CanalRuidoso cr = new CanalRuidoso(e.getTrama(), padraoErros);
			Receptor r = new Receptor(cr.getTrama(), opTecnica);
			tamanhoTrama = r.getTrama().length;
			
			if(tramasIguais(e.getTrama(),r.getTrama()))
				tramasRecebidaSemErros++;
			
			numErrosNaTrama += numErrosNaTrama(cr.getPadraoErro());
			
			if(opTecnica == 1){
				if(!tramasIguais(e.getTrama(),r.getTrama()) && numBitsPar(r.getTrama()))
					erros_nao_detectados ++;
			}else if(opTecnica == 2){
				if(!tramasIguais(e.getTrama(),r.getTrama()) && r.getErrosNaTrama().equals("Trama sem Erro(s)"))
					erros_nao_detectados ++;
			}else if(opTecnica == 3){
				if(tramaComErros(cr.getPadraoErro(), 3))
					erros_nao_detectados ++;
				if(tramaComErros(cr.getPadraoErro()))
					tramaCom1Erro++;
			}

			tramasComErros = NUM_TRAMAS_ENVIADAS - tramasRecebidaSemErros;
		}
		
		prob_tx_sem_erros = ((double)tramasRecebidaSemErros/NUM_TRAMAS_ENVIADAS);
		valor_esperado = ((double)numErrosNaTrama/NUM_TRAMAS_ENVIADAS);
		prob_erros_nao_detectados = ((double)erros_nao_detectados/tramasComErros);
		
		if(opTecnica == 3 && tramasComErros != 0)
			prob_trama_corrigida = (double)tramaCom1Erro/tramasComErros;
		else
			prob_trama_corrigida = 0;
		
		ms.imprimeResultado(dados.length, prob_tx_sem_erros, valor_esperado, prob_erros_nao_detectados, prob_trama_corrigida);
	}

	private boolean numBitsPar(int[] trama) {
		int soma = 0;
		for (int i = 0; i < trama.length; i++) {
			soma += trama[i];
		}
		return soma%2 == 0;
	}

	private boolean tramaComErros(int[] padraoErro) {
		int soma = 0;
		for (int i = 0; i < padraoErro.length; i++) {
			soma += padraoErro[i];
		}
		return soma == 1;
	}
	
	private boolean tramaComErros(int[] padraoErro, int nErros) {
		int soma = 0;
		for (int i = 0; i < padraoErro.length; i++) {
			soma += padraoErro[i];
		}
		return soma >= nErros;
	}

	private boolean tramasIguais(int[] tramaEnviada, int[] tramaRecebida) {
		for (int i = 0; i < tramaEnviada.length; i++) {
			if(tramaEnviada[i] != tramaRecebida[i])
				return false;
		}
		return true;
	}
	
	private int numErrosNaTrama(int[] trama){
		int numErros = 0;
		for (int i = 0; i < trama.length; i++) {
			numErros += trama[i];
		}
		return numErros;
	}
	
}
